/**
 * 用户信息修改路由模块
 * @author HalRui
 * @version 1.0.0
 * @description 处理用户信息修改相关的API请求，包括密码重置、头像更换等功能
 * @date 2025年9月27日
 */

// 引入Express框架，用于创建Web应用和API
const express = require('express');
// 创建Express路由实例，用于定义各种HTTP请求的处理函数
const router = express.Router();
// 引入用户信息操作API模块，提供用户数据的增删改查功能
const userinfosAPI = require("../../libs/userinfos-api");
// 引入状态码常量模块，统一定义API返回的状态码和消息文本
const STATUS_CODES = require("../../utils/status-codes");
// 引入邮箱验证中间件，用于验证邮箱格式的合法性
const emailValidator = require("../../middlewares/email-validator-middleware");
// 引入Redis操作API模块，用于缓存验证码等临时数据
const redisAPI = require("../../libs/redis-api");
// 引入工具服务模块，提供邮件发送等功能
const toolService = require("../../utils/tool-service");
// 引入UUID生成模块，用于生成唯一标识符
const { v4: uuidv4 } = require('uuid');
// 引入SVG验证码生成模块
const svgCaptcha = require("svg-captcha");
// 引入全局 路径
const {PATH_AVATARS} = require("../../globle_path");

const path = require("path");
const fs = require("fs");

/*
* 输入图形验证码 + 修改密码
*
* 弹窗方式的流程
* 1.输入邮箱
* 输入图片验证码 , 验证成功 , 签发返回一个uuid , 前端需要把uuid 使用SessionStorage存储 , 后端中使用redis 保存uuid
* 2.uuid 发送邮箱验证码
* 3.核销邮箱验证码(前端删除SessionStorage)  保留用户信息  删除 redis 中的 uuid
* 4.修改密码
* */
router.get('/forgot-captcha', async (req, res) => {
    try {
        // 1. 从请求头中获取UUID，用于标识不同的验证码会话
        const uuidHead = req.get("forgot-uuid");

        // 2. 生成数学公式验证码
        // 配置验证码参数：长度、字符集、干扰项、颜色等
        const captcha = svgCaptcha.createMathExpr({
            size: 6,                    // 验证码长度（6位）
            ignoreChars: '0o1iIOlL',    // 忽略容易混淆的字符（0、o、1、i、I、O、l、L）
            noise: 4,                   // 干扰线条数量
            color: true,                // 启用彩色验证码
            background: '#f0f0f0',      // 设置背景色为浅灰色
            mathMin: 1,                 // 数学表达式的最小值
            mathMax: 9,                 // 数学表达式的最大值
            mathOperator: '+-'          // 限制运算符类型为加法和减法
        });

        let setResult;  // 存储验证码保存结果
        if(uuidHead){   // 如果请求头中包含UUID
            // 使用现有的UUID保存验证码到Redis（有效期60秒）
            setResult = await redisAPI.setCaptcha(uuidHead, captcha.text)
        }else{          // 如果请求头中没有UUID
            // 生成新的UUID并设置到cookie中
            const id = uuidv4();
            res.cookie("forgot-uuid", id, {
                httpOnly: true,     // 防止XSS攻击
                secure: false,      // 开发环境设为false，生产环境建议设为true
                maxAge: 60000       // 60秒过期
            });     // 将UUID保存到cookie，用于后续验证
            setResult = await redisAPI.setCaptcha(id, captcha.text)
        }

        // 检查验证码是否成功保存到Redis
        if (!setResult.success) {
            return res.status(500).json({
                code: STATUS_CODES.CAPTCHA_SAVE_FAILED.code,      // 30002: 验证码保存失败
                message: STATUS_CODES.CAPTCHA_SAVE_FAILED.message   // 验证码保存失败
            });
        }

        // 返回SVG格式的验证码图片给客户端
        res.type('svg');                // 设置响应内容类型为SVG
        res.status(200).send(captcha.data);  // 发送验证码图片数据
    } catch (error) {
        // 捕获异常并记录错误日志
        console.error('生成验证码失败:', error);
        // 返回生成失败的状态码
        res.status(500).json({
            code: STATUS_CODES.CAPTCHA_GENERATE_FAILED.code,      // 30003: 验证码生成失败
            message: STATUS_CODES.CAPTCHA_GENERATE_FAILED.message   // 验证码生成失败
        });
    }
});

//发送邮件
router.post("/forgot-sendmail", async (req, res) => {
    //获取cookie中的
    const forgotUUID = req.cookies["forgot-uuid"];
    //获取邮箱
    const {mail,code} = req.body;
    //查看uuid有没有过期
    const redisCodeResult = await redisAPI.getCaptcha(forgotUUID);
    if(!redisCodeResult.success || !redisCodeResult.message || code.toLowerCase() !== redisCodeResult.message.toLowerCase()){  // 如果验证码错误（忽略大小写）
        return res.json({
            code: STATUS_CODES.CAPTCHA_ERROR.code,        // 10003: 验证码输入有误
            message: STATUS_CODES.CAPTCHA_ERROR.message   // 对应的消息文本
        })
    }

    //发送邮件
    // 生成8位数字验证码
    const vcode = toolService.createCode(8);

    // 将验证码保存到Redis缓存中，设置过期时间
    const saveResult = await redisAPI.saveEmailCode(mail, vcode);

    if (!saveResult.success) {
        // 如果保存失败，返回发送失败状态码
        return res.json({
            code: STATUS_CODES.EMAIL_CODE_SEND_FAILED.code,      // 使用状态码常量：邮箱验证码发送失败
            message: STATUS_CODES.EMAIL_CODE_SEND_FAILED.message   // 对应的消息文本
        });
    }

    // 调用邮件服务发送验证码到用户邮箱，设置3秒超时
    try {
        const info = await Promise.race([
            toolService.sendMailCode(mail, vcode),
            new Promise((_, reject) => 
                setTimeout(() => reject(new Error('邮件发送超时')), 3000)
            )
        ]);

        //删除cookie内容  删除redis中captcha
        if(info.success){ // 邮件发送成功
            // 返回成功响应，包含邮箱地址信息
            res.json({
                code: STATUS_CODES.SUCCESS.code,      // 使用状态码常量：操作成功
                message: "邮箱验证码发送成功",           // 自定义成功消息
                data: { email: mail }                 // 返回邮箱地址数据
            })
        }else{ // 邮件发送失败
            // 返回发送失败状态码
            res.json({
                code: STATUS_CODES.EMAIL_CODE_SEND_FAILED.code,      // 使用状态码常量：邮箱验证码发送失败
                message: STATUS_CODES.EMAIL_CODE_SEND_FAILED.message   // 对应的消息文本
            })
        }
    } catch (error) {
        // 邮件发送超时或异常，但验证码已保存到Redis，返回成功
        console.log(`忘记密码邮件发送失败但验证码已保存: ${error.message}`);
        res.json({
            code: STATUS_CODES.SUCCESS.code,
            message: "验证码已生成，请查收邮件（如未收到请稍后重试）",
            data: { email: mail }
        });
    }
})


//忘记密码
router.post("/forgot-verify",
    emailValidator.validateEmail,
    async (req,res)=>{
        //1.接受一个邮箱地址 验证邮箱格式  cookie
        const {mail,code} = req.body;

        //2.判断当前用户是否存在
        const info = await userinfosAPI.verifyEmail(mail);
        if(!info.success){
            return res.json({
                code : STATUS_CODES.USER_NOT_FOUND.code,
                message : STATUS_CODES.USER_NOT_FOUND.message
            })
        }

        //3.验证图片的验证码 验证
        const redisCodeResult = await redisAPI.getEmailCode(mail);

        if(!redisCodeResult.success || code.toLowerCase() !== redisCodeResult.message.toLowerCase()){  // 如果验证码错误（忽略大小写）
            return res.json({
                code: STATUS_CODES.CAPTCHA_ERROR.code,        // 10003: 验证码输入有误
                message: STATUS_CODES.CAPTCHA_ERROR.message   // 对应的消息文本
            })
        }

        //4.输入的邮箱 放到 mongodb中获取用户信息
        const user = await userinfosAPI.findUserByEmail(mail);
        if(!user.success){
            res.json({
                code : STATUS_CODES.GET_USER_INFO_FAILED.code,
                message : STATUS_CODES.GET_USER_INFO_FAILED.message
            })
        }

        //删除redis 中的 emailcode
        await redisAPI.dropEmailCode(`email_code:${mail}`);

        //5.返回用户信息
        res.json({
            code : STATUS_CODES.SUCCESS.code,
            message : "验证成功",
            data : user.message
        })
    })

//修改密码
router.post("/change-pass",
    async (req,res)=>{
        const {userId,pass,confirm} = req.body;

        if(pass !== confirm){
            return res.json({
                code: STATUS_CODES.PASSWORD_NOT_MATCH.code,      // 20005: 两次输入的密码不一致
                message: STATUS_CODES.PASSWORD_NOT_MATCH.message   // 两次输入的密码不一致
            })
        }

        const result = await userinfosAPI.changePassword(userId,pass);

        if(!result.success){
            return res.json({
                code : STATUS_CODES.UPDATE_PROFILE_FAILED.code,
                message : STATUS_CODES.UPDATE_PROFILE_FAILED.message
            })
        }

        res.json({
            code : STATUS_CODES.SUCCESS.code,
            message : "用户信息更新成功"
        })
    })


/*
* 1.default-avatar.png  默认头像 需要 注意
* 2.上传一个图形进来 , 验证文件的 后缀名称
* 3.检测 当前 用户的头像路径 是否存在
*   不存在 则创建
* 4.接受上传的头像 , 放置到 固定 位置
* 5.修改 数据库 信息
*
* 如果是默认的 头像 , 则不删除 , 不是则删除
* */
//修改用户头像
router.post("/change-avatar",async (req, res, next)=>{
    const {userId} = req.fields;
    const {avatar} = req.files;

    req.locals = {
        filePath : "",
    }

    // 验证文件是否存在
    if(!avatar || avatar.size === 0){
        return res.json({
            code : STATUS_CODES.UPLOAD_ERROR.code,
            message : "请选择要上传的头像文件",
        })
    }

    // 验证文件大小（限制为5MB）
    const maxSize = 5 * 1024 * 1024; // 5MB
    if(avatar.size > maxSize){
        return res.json({
            code : STATUS_CODES.UPLOAD_ERROR.code,
            message : "文件大小不能超过5MB",
        })
    }

    // 验证userId格式，防止路径遍历攻击
    if(!userId || !/^[a-zA-Z0-9_-]+$/.test(userId)){
        return res.json({
            code : STATUS_CODES.UPLOAD_ERROR.code,
            message : "用户ID格式不正确",
        })
    }

    //检查文件的后缀名称（更严格的验证）
    const ext = path.extname(avatar.name).toLowerCase();
    const allowedExts = [".png", ".jpg", ".jpeg", ".webp", ".bmp"];
    if(!allowedExts.includes(ext)){
        return res.json({
            code : STATUS_CODES.UPLOAD_ERROR.code,
            message : "只支持PNG、JPG、JPEG、WEBP、BMP格式的图片文件",
        })
    }

    // 验证文件MIME类型
    const allowedMimeTypes = ['image/png', 'image/jpeg', 'image/webp', 'image/bmp'];
    if(!allowedMimeTypes.includes(avatar.type)){
        return res.json({
            code : STATUS_CODES.UPLOAD_ERROR.code,
            message : "文件类型不正确",
        })
    }

    // 使用安全的文件名，防止路径遍历
    const safeUserId = userId.replace(/[^a-zA-Z0-9_-]/g, '');
    const userPath = path.join(PATH_AVATARS, safeUserId);

    if(!fs.existsSync(userPath)){//路径不存在则创建
        fs.mkdirSync(userPath,{recursive:true});
    }

    // 生成安全的文件名
    const safeFileName = `${safeUserId}-${Date.now()}-${uuidv4()}${ext}`;
    const filePath = path.join(userPath, safeFileName);

    try{
        fs.copyFileSync(avatar.path, filePath);
        fs.unlinkSync(avatar.path);

        req.locals.filePath = filePath;
    }catch(e){
        return res.json({
            code : STATUS_CODES.UPLOAD_ERROR.code,
            message : STATUS_CODES.UPLOAD_ERROR.message,
        });
    }

    next();
},
async (req,res)=>{

    const {userId} = req.fields;

    //原来的图片地址进行删除
    const user = await userinfosAPI.findByUserId(userId);
    if(user.message.avatar !== "/public/images/default-avatar.png"){
        try{
            fs.unlinkSync(user.message.avatar);
        }catch(e){
            console.log(`${user.message.avatar}无法被删除或不存在`);
        }

    }

    //数据库信息要更新了
    const result = await userinfosAPI.changeAvatar(userId,req.locals.filePath);

    if(result.success){
        res.json({
            code : STATUS_CODES.SUCCESS.code,
            message : "头像修改成功"
        })
    }else{
        res.json({
            code : STATUS_CODES.PULISH_ERROR.code,
            message : STATUS_CODES.PULISH_ERROR.message
        })

        fs.unlinkSync(req.locals.filePath);
    }
})


module.exports = router;